#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include "client.h"
 
#define SER_IP       "127.0.0.1"
#define SER_PORT     6789
 
#define LISTEN_MAX   100
char myname[20]={0};
char signname[100]={0};
char account_c[100]={0};
char mylocalname[20]; //用于保存传输文件来源的名字
char fsignname[40]; //用于保存文件传输名字
int root=0;
int flag = 1;
void *recvdata_do_handler(void *a);
/********************************************
 * TCP通信客户端流程
 * 1，创建套接字
 * 2，请求连接
 * 3，收发数据
 * 4，关闭套接字
 * *************************************************/
 
int main(int argc, char  *argv[])
{
    //1.创建套接字   IPV4  流式套接字  TCP协议
    int clifd = socket(AF_INET,SOCK_STREAM,0);
    if(clifd == -1)
    {
        perror("socket");
        return -1;
    }
    printf("clifd = %d\n",clifd);
 
 
    struct sockaddr_in seraddr;
    memset(&seraddr,0,sizeof(struct sockaddr_in));
    seraddr.sin_family = AF_INET;//ipv4协议类型
    seraddr.sin_addr.s_addr = inet_addr(SER_IP);
    seraddr.sin_port = htons(SER_PORT);//端口    网络字节序
    //2.请求连接
    if(connect(clifd,(struct sockaddr *)&seraddr,sizeof(struct sockaddr_in)) == -1)
    {
        perror("connect");
        return -1;
    }
   
    printf("连接成功\n");
   
 
    
    Ask_server(clifd);
    close(clifd);
    return 0;
}
 
 
 
void main_menu(void)
{
	system("clear");
	printf("\n\n\n\n\n\n\n\n");
	printf("+++++++++++++++++++++++++++++++++++++++++\n");
	printf("+++\t    欢迎使用101聊天室      +++\n");
	printf("+++\t\t1,注册                +++\n");
	printf("+++\t\t2,登录                +++\n");
	printf("+++\t\t3,退出                +++\n");
	printf("+++++++++++++++++++++++++++++++++++++++++\n");
 
}
 
int Ask_server(int client_socket)
{
	char ch;
	int ret;
	while(1)
	{
		main_menu();//引用菜单栏
		printf("请输入您要做操作\n");
		scanf("%c",&ch);
		while(getchar() != '\n');
		switch(ch)
		{
			case '1':    //注册
				regisc(client_socket);
				sleep(1);
				break;
			case '2':    //登录
				ret = entry(client_socket);
				if (ret == 1)
				{
					User_used(client_socket);    //调用函数表示用户界面
				}
				sleep(1);
				break;
			case '3':    //退出
				exit(0);
				break;
		}
	}
}
//注册函数
void regisc(int client_socket)
{
    Msg msg;
	memset(&msg,0,sizeof(msg));
    msg.cmd=1;
    printf("请输入用户名\n");
    scanf("%s",msg.fromname);
    while(getchar() != '\n');
    printf("请输入账号\n");
    scanf("%s",msg.account);
    while(getchar() != '\n');
    printf("请输入密码\n");
    scanf("%s",msg.password);
	 while(getchar() != '\n');
    printf("请输入个性签名\n");
    scanf("%s",msg.signname);
	 while(getchar() != '\n');
    send(client_socket,&msg,sizeof(msg),0);
    recv(client_socket,&msg,sizeof(msg),0);
	printf("%d\n",msg.cmd);
   if(msg.cmd == 1001)
	{
		printf("注册成功\n");
	}
	else if (msg.cmd == -1)
	{
		printf("用户名以存在,注册失败\n");
	}
	else
	{
		printf("系统繁忙,注册失败\n");
	}
	
	sleep(2);
 
 
}
 
 
//登录账号
int entry(int client_socket)
{
	Msg msg;
	memset(&msg,0,sizeof(msg));
	msg.cmd = 2;
	
	printf("登录,请输入账号名: ");
	scanf("%s",msg.account);
	while(getchar() != '\n');
	
	printf("登录,请输入密码: ");
 
	
 
	scanf("%s",msg.password);
	while(getchar() != '\n');
 
	printf("\n");
 
	write(client_socket, &msg, sizeof(msg));
	memset(&msg,0,sizeof(msg));
	read(client_socket, &msg, sizeof(msg));
	printf("%d\n",msg.cmd);
	if(msg.cmd == -1)    //表示用户不存在
	{
		printf("登录失败,系统繁忙.\n");
		sleep(2);
		return -1;
	}
	if(msg.cmd == -2)    //表示用户不存在
	{
		printf("登录失败,用户不存在.\n");
		sleep(2);
		return -2;
	}
	if(msg.cmd == -3)
	{
		printf("登录失败,用户已登录.\n");
		sleep(2);
		return -3;
	}	
	if(msg.cmd == -4)
	{
		printf("登录失败,密码错误.\n");
		sleep(2);
		return -4;
	}	
	if(msg.cmd == 1002)
	{
		printf("登录成功,登录中...\n");
		printf("%s %s \n",msg.fromname,msg.signname);
		 strcpy(myname,msg.fromname);      //保存在线名字
		 strcpy(signname, msg.signname);   //保存个性签名
		 strcpy(account_c,msg.account);    //保存账号
		 root=msg.num; //获取权限
 
 
		sleep(2);
		return 1;
	}
}
 
void user_menu(void)
{
	system("clear");
	printf("%s: %s\n",myname,signname);
	//printf("\n\n\n\n\n\n\n");
	printf("++++++++++++++++++++++++++++++++++++++++\n");
	printf("+++                                  +++\n");
	printf("+++\t   欢迎使用101聊天室      +++\n");
	printf("+++                                  +++\n");
	printf("++++++++++++++++++++++++++++++++++++++++\n");
	printf("+++\t\t1,群聊               +++\n");
	printf("+++\t\t2,私聊               +++\n");
	printf("+++\t\t3,退出登录           +++\n");
	printf("+++\t\t4,查看聊天记录       +++\n");
	printf("+++\t\t5,显示当前在线人员   +++\n");
	printf("+++\t\t6,修改个性签名       +++\n");
	printf("+++\t\t7,修改密码           +++\n");
	printf("+++\t\t8,传输文件           +++\n");
	printf("+++\t\t9,注销账号           +++\n");
	printf("+++\t\tA,管理           +++\n");
	printf("++++++++++++++++++++++++++++++++++++++++\n");
}
 
//用户界面
void User_used(int client_socket)
{
	//要进行读写分离
	
	pthread_t read_id;
	pthread_create(&read_id, NULL, readMsg, (void *)&client_socket);
	pthread_detach(read_id);   //等待线程分离
	
	char ch;
	flag=1;
	while(flag)
	{
		user_menu();//调用第二个用户界面
		printf("请输入您要做操作\n");
		scanf("%c",&ch);
		while(getchar() != '\n');
		switch(ch)
		{
			case '1':        //群聊
				chat_all(client_socket);
				break;
			case '2':        //私聊
				chat_one(client_socket);
				break;
			case '3':        //退出登录	
				entry_out(client_socket);
				flag = 0;
				break;
			 case '4':        //查看聊天记录
			 	look_chat();  
			 	break;
			 case '5':        //显示当前在线人数 
			 	see_now_time(client_socket);
			 	break;
			 case '6':        //修改个性签名
			 	revise_sign(client_socket);
			 	break;
			 case '7':        //修改密码
			 	revise_password(client_socket);
				sleep(1);	
			 	break;
			 case '8':        //传输文件
			 	transfer_file(client_socket);
			 	break;
			 case 'y':        //表示愿意接受文件
			 	transfer_file_y(client_socket);
				printf("接收中\n");
				getchar();
			 	break;
			 case 'n':        //表示不愿意接受文件
			 	transfer_file_n(client_socket);sleep(1);
			 	break;		
			case '9':client_logout(client_socket);//表示注销账号
			flag=0;break;
			case 'a':if(root==1)//判断是不是管理员
			client_guanli(client_socket);
			else
			printf("管理失败没有权限\n");
			sleep(1);
			break;
		}
	}
 
	 //pthread_cancel(read_id);
}
 
//读写分离专门收消息
void * readMsg (void *v)
{
	int client_socket = *((int*)v);
	Msg buf;
	time_t t;
	struct tm *pt;
	int i = 0;
	while(1)
	{
		memset(&buf,0,sizeof(Msg));
		int ret = read(client_socket, &buf, sizeof(Msg));
		if(ret == -1)
		{
			perror("read");
			break;
		}
		switch(buf.cmd)
		{
			case 3:  
				printf(" %s\n",buf.msg);
				save(&buf);    //保存聊天记录
				break;
			 case 4:   //私聊
			
			 	printf(" %s\n",buf.msg);
			 	save(&buf);    //保存聊天记录
			 	break;
			 case -3:  //私聊失败
			 	printf("私聊失败,用户不存在或下线\n");
			 	break;
			 case 5 :  //退出登录
			 	printf("%s 退出登录\n",buf.fromname);
			 	sleep(1);
			 	pthread_exit(NULL);    //线程退出
			 	break;
			 case 6 :   //显示当前在线人数
			 	printf("当前在线人员:\n");
			 	printf("%s\n",buf.msg);
			 	break;
			 case 7 :  //修改个性签名成功
			 	strcpy(signname,buf.signname);
			 	printf("修改个性签名成功\n");
			 	break;
			 case -7 :  //修改个性签名失败
			 	printf("修改个性签名失败\n");
			 	break;
			 case 8 	:   //修改密码成功
			 	printf("修改密码成功\n");
				entry_out(client_socket);
				flag=0;
			 	break;
			 case -8 : 	//修改密码失败
			 	printf("修改密码失败\n");
			 	break;
			 case 9  :   //调用函数确认是否接受文件
			 	system("clear");
			 	printf("请问你是否接受来自 %s 的文件 %s(y/n)\n",buf.fromname,buf.signname);
			 	strcpy(mylocalname,buf.fromname);   //保存传输文件来源名字
		 		sleep(1);
			 	break;
			 case -9 :   //表示传输文件失败,没有找到该人
			 	printf("发送文件失败,好友不在线或不存在\n");sleep(1);
			 	break;
			case 10 :   //表示愿意接受文件,开始传输
				start_transfer_file(client_socket);
				sleep(1);
				break;
			case -10 :  //表示不愿意接受文件
				printf("发送文件失败,好友拒绝接受文件\n");sleep(1);
				break;
			case 11 :   //接受文件
				save_transfer_file(&buf);
				
				break;
				//printf("%d\n",i);
			case 22:printf("%s\n",buf.msg);
 
			break;
			case 87:printf("禁言成功\n");
			break;
			case 88:printf("踢人成功\n");
			break;
			case 99:printf("注销成功\n");
			sleep(1);
			 	pthread_exit(NULL);    //线程退出
			 	break;
			default:break;
		}
 
	}
}
 
void entry_out(int client_socket)//退出登录
{
	Msg msg;
	memset(&msg,0,sizeof(msg));
	msg.cmd=5;
	strcpy(msg.account,account_c);
	strcpy(msg.fromname,myname);
	send(client_socket,&msg,sizeof(msg),0);
	sleep(1);
	
}
 
void chat_all(int client_socket)//群聊
{
	system("clear");
	Msg msg;
	time_t t;
	char senr[150];
	memset(&msg,0,sizeof(msg));
	msg.cmd = 3;
	strcpy(msg.fromname,myname);
	strcpy(msg.localname,"All");
	printf("输入quit退出\n");
	printf("请输入你要群发送的信息\n");
	while(1)
	{
	
	memset(msg.msg,0,sizeof(msg.msg));
	memset(senr,0,sizeof(senr));
	// getchar();
	time(&t);
	struct tm *pt = localtime(&t);
	getchar();
	printf("%d年%d月%d日 %d:%d:%d\n",pt->tm_year+1900,pt->tm_mon+1,pt->tm_mday,
	pt->tm_hour,pt->tm_min,pt->tm_sec);
 
	sprintf(msg.msg,"\t\t\t%d年%d月%d日 %d:%d:%d\n\t\t\t",pt->tm_year+1900,pt->tm_mon+1,pt->tm_mday,
	pt->tm_hour,pt->tm_min,pt->tm_sec);
	 
	printf("%s:",msg.fromname);
	scanf("%s",senr);
	strcat(msg.msg,msg.fromname);
	strcat(msg.msg,":");
	strcat(msg.msg,senr);
	while(getchar() != '\n');
	if(strcmp("quit",senr)==0)
	break;
	write(client_socket, &msg, sizeof(Msg));
	}
	save(&msg);
 
	sleep (2);
}
 
void chat_one(int client_socket)//私聊
{
	system("clear");
	Msg msg;
	char senr[150];
	time_t t;
	memset(&msg,0,sizeof(msg));
	msg.cmd = 4;
	printf("请输入你要聊天的对象:\n");
	scanf ("%s",msg.localname);
	while(getchar() != '\n');
	strcpy (msg.fromname,myname);
	while(1)
	{
		getchar();
	time(&t);
	struct tm *pt = localtime(&t);
	printf("%d年%d月%d日 %d:%d:%d\n",pt->tm_year+1900,pt->tm_mon+1,pt->tm_mday,
	pt->tm_hour,pt->tm_min,pt->tm_sec);
	sprintf(msg.msg,"\t\t\t%d年%d月%d日 %d:%d:%d\n\t\t\t",pt->tm_year+1900,pt->tm_mon+1,pt->tm_mday,
	pt->tm_hour,pt->tm_min,pt->tm_sec);
	
	
	
	printf("%s:",msg.fromname);
	scanf("%s",senr);
	while(getchar() != '\n');
	if(strcmp("quit",senr)==0)
	{
		break;
	}
 
	strcat(msg.msg,msg.fromname);
	strcat(msg.msg,":");
	strcat(msg.msg,senr);
	
	
	
	write(client_socket, &msg, sizeof(Msg));
 
	save(&msg);    //保存聊天记录
	}
	sleep(2);
}
 
 
void client_logout(int client_socket)//注销
{
	Msg msg;
	memset(&msg,0,sizeof(msg));
	msg.cmd=99;
	strcpy (msg.account,account_c);
	send(client_socket,&msg,sizeof(msg),0);
}
 
void save(Msg*msg)//保存
{
	char buf2[120];
	char ai[1050];
	memset(buf2,0,sizeof(buf2));
	memset(ai,0,sizeof(ai));
	if(strcmp(myname,msg->fromname)==0)
	{
	sprintf(buf2,"../liaotianjilu/%s & %s.txt",msg->fromname,msg->localname);
	}
	else
	{
	sprintf(buf2,"../liaotianjilu/%s & %s.txt",msg->localname,msg->fromname);
	}	
	
	int ps=open(buf2,O_WRONLY|O_CREAT|O_APPEND,0644);
	sprintf(ai,"%s\n",msg->msg);
	write(ps,ai,strlen(ai));
	close (ps);
 
}
 
void look_chat()//查看聊天记录
{
	char buf2[120];
	char ai[10500];
	memset(ai,0,sizeof(ai));
	memset(buf2,0,sizeof(buf2));
	printf("查看与谁的聊天记录：\n");
	scanf("%s",buf2);
	while(getchar()!='\n');
	sprintf(ai,"../liaotianjilu/%s & %s.txt",myname,buf2);
	
	int ps=open(ai,O_RDONLY);
	if(ps==-1)
	{
		printf("查看失败\n");
		sleep(1);
		return;
	}
	memset(ai,0,sizeof(ai));
	read(ps,ai,sizeof(ai));
	printf("%s\n",ai);
	getchar();
	close(ps);
 
	
}
 
void see_now_time(int client_socket)//查看在线人数
{
	Msg msg;
	memset(&msg,0,sizeof(msg));
	msg.cmd = 6;
	strcpy (msg.fromname,myname);
	write(client_socket, &msg, sizeof(Msg));
	
	sleep(2);
}
 
void client_guanli(int client_socket)//管理
{
	Msg msg;
	memset(&msg,0,sizeof(msg));
	printf("请选择：1禁言，2踢人\n");
	int i;
	scanf("%d",&i);
	while(getchar()!='\n');
	if(i==1)
	{
	msg.cmd=87;
	printf("请选择要禁言的人:\n");
	see_now_time(client_socket);
	scanf("%s",msg.localname);
	
	
 
	}
	else if(i==2)
	{
		msg.cmd=88;
		printf("请选择要踢的人:\n");
	see_now_time(client_socket);
	scanf("%s",msg.localname);
	
	
	}
	else
	{
		printf("管理失败\n");
		return;
	}
	strcpy (msg.fromname,myname);
	write(client_socket, &msg, sizeof(Msg));
	
	sleep(1);
}
 
//修改个性签名
void revise_sign(int client_socket)
{
	Msg msg;
	memset(&msg,0,sizeof(msg));
	msg.cmd = 7;
	printf("请输入新的个性签名:");
	scanf("%s",msg.signname);
	while(getchar() != '\n');
	
	strcpy(msg.fromname,myname);   //需要保存名字
	strcpy(msg.account,account_c);
	write(client_socket, &msg, sizeof(Msg));
 
	sleep(2);
}
 
//修改密码
void revise_password(int client_socket)
{
	Msg msg;
	memset(&msg,0,sizeof(msg));
	msg.cmd = 8;
	printf("请输入新的密码: ");
	scanf("%s",msg.password);
	while(getchar() != '\n');
	
	strcpy(msg.fromname,myname);   //需要保存名字
	strcpy(msg.account,account_c);
	write(client_socket, &msg, sizeof(Msg));
	
	sleep(2);
}	
 
void transfer_file(int client_socket)//传输文件
{
	Msg msg;
	msg.cmd = 9;
	printf("请输入你要传输文件的对象:");
	scanf ("%s",msg.localname);
	while(getchar() != '\n');
	
	printf("请输入你要传输的本地文件名:");
	scanf ("%s",msg.signname);
	while(getchar() != '\n');
	
	strcpy(msg.fromname,myname);   //需要保存名字
	write(client_socket, &msg, sizeof(Msg));
	
	printf("等待验收中...\n");
	
	strcpy(fsignname,msg.signname);
	strcpy(mylocalname,msg.localname);
	getchar();
	
}
 
 
//表示愿意接受文件
void transfer_file_y(int client_socket)
{
	Msg msg;
	msg.cmd = 10;
	strcpy(msg.fromname,myname); 
	strcpy(msg.localname,mylocalname); 
	strcpy(mylocalname,"\0");    //用完后置零
	write(client_socket, &msg, sizeof(Msg));
}
 
//表示不愿意接受文件
void transfer_file_n(int client_socket)
{
	Msg msg;
	msg.cmd = -10;
	strcpy(msg.fromname,myname); 
	strcpy(msg.localname,mylocalname); 
	strcpy(mylocalname,"\0");    //用完后置零
	write(client_socket, &msg, sizeof(Msg));
}
 
//传出文件来源开始传输文件
void start_transfer_file(int client_socket)
{
	Msg msg;
	char ai[120];
	msg.cmd = 11; 
	strcpy(msg.fromname,myname); 
	strcpy(msg.signname,fsignname);
	strcpy(msg.localname,mylocalname);
	sprintf(ai,"../send/%s",msg.signname);
	int fd = open(ai,O_RDONLY); 
	if(fd == -1)
	{
		perror("open");
		printf("传输失败\n");
		return ;
	}
	
	int ret = 0;
	int i = 0;
	while(ret = read(fd,msg.msg,SIZE))
	{
		if(ret == -1)
		{
			perror("read");
			break;
		}
		if (ret == 0)
		{
			break;
		}
		msg.num = ret;
		write(client_socket, &msg, sizeof(Msg));
		usleep(10000);     //这个睡眠时间减缓传输速度,降低不可预祝错误出现
	}
	printf("文件复制完成传输\n");
	strcpy(fsignname,"\0");
	strcpy(mylocalname,"\0");
	
	close(fd);
}
 
//接受文件
void save_transfer_file(Msg * buf)
{
	char ai[120];
	sprintf(ai,"../recv/%s",buf->signname);
	int fd = open(ai,O_WRONLY|O_CREAT|O_APPEND,0777);
	write(fd,buf->msg,buf->num);
	if(buf->num != SIZE)
	{
		printf ("文件接受完成\n");
	}
	close (fd);
	
}